/**
 * @file clientUDP.c
 *
 * @date Mar 31, 2012
 * @author Francesco Corvino
 */
#include <stdio.h>      /* standard C i/o facilities */
#include <stdlib.h>     /* needed for atoi() */
#include <unistd.h>  	/* defines STDIN_FILENO, system calls,etc */
#include <sys/types.h>  /* system data type definitions */
#include <sys/socket.h> /* socket specific definitions */
#include <netinet/in.h> /* INET constants and stuff */
#include <arpa/inet.h>  /* IP address conversion stuff */
#include <netdb.h>	/* gethostbyname */
#include "../src/MemObj/dynArray.h"

#define MAXBUF 1024*1024
#define CHATSTART_MESSAGE "start"

typedef struct sockaddr_in Address;

typedef struct utente {
	char name[20];
	Address address;
} Utente;

DynArray utenti;

int Utente_cmp(const void * a, const void * b){
	Utente *A = (Utente*) a; Utente *B = (Utente*) b;
	printf("Confronto tra %s:%d e %s:%d\n",
			inet_ntoa(A->address.sin_addr), ntohs(A->address.sin_port),
			inet_ntoa(B->address.sin_addr), ntohs(B->address.sin_port));
	return memcmp(&(A->address),&(B->address),sizeof(Address));
	//return strcmp(&(A->name),&(B->name));
}

/**
 * @brief Attende un'input tra due file descriptor.
 *
 * @return restituisce il primo file descriptor che è pronto per la lettura.
 */
int WaitInput(int fd0, int fd1) {
	struct timeval tv;
	tv.tv_sec = 1000;
	tv.tv_usec = 0;
	fd_set fdsetread;
	FD_ZERO(&fdsetread);
	FD_SET(fd0, &fdsetread);
	FD_SET(fd1, &fdsetread);
	int maxfd = (fd0 > fd1) ? (fd0) : (fd1);
	select(maxfd + 1, &fdsetread, NULL, NULL, &tv);
	if (FD_ISSET(fd0,&fdsetread))
		return fd0;
	else if (FD_ISSET(fd1,&fdsetread))
		return fd1;
	return -1;
}

/**
 * @brief Funzione che gestisce la scrittura sullo standard output.
 */
void WriteMsg(char* name, char *msg) {
	printf("%s: %s \n", name, msg);
}

/**
 * @brief Inizializza i dati di rete restituendo il socket su cui effettuare
 * le comunicazioni.
 */
int StartNetData() {
	int ld;
	struct sockaddr_in skaddr;
	int length;

	/* create a socket IP protocol family (PF_INET) UDP protocol (SOCK_DGRAM) */
	if ((ld = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		printf("Problem creating socket\n");
		exit(1);
	}

	skaddr.sin_family = AF_INET;
	skaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	skaddr.sin_port = htons(0);

	if (bind(ld, (struct sockaddr *) &skaddr, sizeof(skaddr)) < 0) {
		printf("Problem binding\n");
		exit(0);
	}

	/* find out what port we were assigned and print it out */

	length = sizeof(skaddr);
	if (getsockname(ld, (struct sockaddr *) &skaddr, &length) < 0) {
		printf("Error getsockname\n");
		exit(1);
	}

	/* port number's are network byte order, we have to convert to
	 host byte order before printing !
	 */
	printf("The server UDP port number is %d\n", ntohs(skaddr.sin_port));
	return ld;
}

/**
 * @brief Avvia un echo server sul socket passato.
 *
 * @param sd socket descriptor.
 */
void echo(int sd) {
	int len, n;
	char bufin[MAXBUF];
	struct sockaddr_in remote;

	// need to know how big address struct is, len must be
	// set before the call to recvfrom!!!
	len = sizeof(remote);

	while (1) {
		/* read a datagram from the socket (put result in bufin) */
		n = recvfrom(sd, bufin, MAXBUF, 0, (struct sockaddr *) &remote, &len);

		/* print out the address of the sender */
		printf("Got a datagram from %s port %d\n", inet_ntoa(remote.sin_addr),
				ntohs(remote.sin_port));

		if (n < 0) {
			perror("Error receiving data");
		} else {
			printf("GOT %d BYTES\n", n);
			/* Got something, just send it back */
			sendto(sd, bufin, n, 0, (struct sockaddr *) &remote, len);
		}
	}
}

Utente * ChatRecv(int sd, char* bufin){
	Address remote; int len= sizeof(Address);
	int n = recvfrom(sd, bufin, MAXBUF, 0,
			(struct sockaddr *) &remote, &len);
	Utente nuovo;
	memcpy(&(nuovo.address),&remote, sizeof(Address));
	Utente *ret = DynArray_get(&utenti, &nuovo);
	if(ret != NULL) return ret;
	else {
		len = strlen(CHATSTART_MESSAGE);
		if(strncmp(bufin,CHATSTART_MESSAGE,len)==0) {
			strcpy(nuovo.name,bufin+len+1);
		}
		if(strlen(nuovo.name)<1)
			sprintf(nuovo.name, "utente%d",utenti.numelement +1);
		DynArray_add(&utenti,&nuovo);
		return DynArray_get(&utenti, &nuovo);
	}
}

/**
 * @brief Invia un messaggio al client.
 */
void ChatSend(int sd,char* msg, int bytes, Utente * client){
	int len = sizeof(Address);
	sendto(sd, msg, bytes, 0,
			(struct sockaddr*) &(client->address), len);
	printf("Messaggio inviato a %s \n",client->name);
}

/**
 * @brief server main routine
 */
int main() {
	int socketFd;
	char msg[MAXBUF];
	DynArray_init(&utenti, Utente_cmp, sizeof(Utente));

	socketFd = StartNetData();

	while(1){
		memset(msg,0,MAXBUF);
		int fdToRead = WaitInput(socketFd,STDIN_FILENO);
		if(fdToRead==socketFd) {
			// lettura dalla rete
			Utente * utente = ChatRecv(socketFd,msg);
			WriteMsg(utente->name, msg);
		} else {
			// lettura dal client
			fgets(msg, MAXBUF, stdin);
			Utente * utente = NULL; int index=0;
			DYNARRAY_FOREACH(&utenti,&index,utente){
				ChatSend(socketFd,msg,strlen(msg),utente);
			}
			puts("---------------- Messaggio inviato -----------------\n");
		}
	}
	return (0);
}

